#include "Charactere.h"
#include "Combinaison.h"
#include "Chapeau.h"
#include "Bague.h"
#include "Collier.h"
#include "Ceinture.h"
#include "Armure.h"
#include "Bottes.h"
#include "Charactere.h"
#include "Sac.h"
#include "Objet.h"
#include "ObjetNonEquipable.h"
#include <sstream>
#include <iomanip>
#include <fstream>


using namespace std;


int main(){

    Charactere * lutin_des_prairie_du_sud_de_la_mer_du_nord;
    lutin_des_prairie_du_sud_de_la_mer_du_nord = new Charactere(7.0,8.8,6.0,90);

    std::vector<Objet *> sol;

    Armure * armure_Dark_Vador;
    armure_Dark_Vador = new Armure("armure_Dark_Vador",20.0,4.0,5.8, 8);

    Armure * armure_du_cavalier_rouge;
    armure_du_cavalier_rouge = new Armure("armure_du_Cavalier_Rouge",8.12,34.1,7,3);

    Bague * bague_dentaire;
    bague_dentaire = new Bague("bague_Dentaire",3.45,2.7452,107.9, 2);

    Bottes * bottes_en_cuire;
    bottes_en_cuire = new Bottes("bottes_en_cuire",34.3,45,8.1,2);

    Ceinture * ceinture_du_profeseur_chen;
    ceinture_du_profeseur_chen = new Ceinture("ceinture_prof_Chen",3.0,-4.5,9,1);

    Chapeau * chapeau_melon;
    chapeau_melon = new Chapeau("chapeau_melon",4,4.5,667.1,4);

    Collier * collier_de_la_professier_de_la_pomme_de_pin;
    collier_de_la_professier_de_la_pomme_de_pin = new Collier("collier_de_la_professier_de_la_pomme_de_pain", 4.003,5.3,-20.8,2);

    Sac * sac_a_main_de_femme_de_joie;
    sac_a_main_de_femme_de_joie = new Sac(500,500);

    Sac * sac_a_rtoon;
    sac_a_rtoon = new Sac(63,87);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->PrendreSac(*sac_a_main_de_femme_de_joie);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->mySacDispo.push_back(sac_a_rtoon);

    lutin_des_prairie_du_sud_de_la_mer_du_nord->EquipementCourant->equiper(*armure_Dark_Vador);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->EquipementCourant->equiper(*bague_dentaire);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->EquipementCourant->equiper(*bottes_en_cuire);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->EquipementCourant->equiper(*ceinture_du_profeseur_chen);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->EquipementCourant->equiper(*chapeau_melon);
    lutin_des_prairie_du_sud_de_la_mer_du_nord->EquipementCourant->equiper(*collier_de_la_professier_de_la_pomme_de_pin);

    //a present, le joueur va pouvoir effectuer les actions qu'il souhaite dans le jeu
    int rep = 9999;
    lutin_des_prairie_du_sud_de_la_mer_du_nord->shell();
    
}
/**
 *execute les commandes speciales du programme
 */
void Charactere::shell(){
    cout << "Tapez list pour une liste des commandes disponibles" << endl;
    string str;
    string buf;
    stringstream ss("dummy");
    vector<string> tokens;
    while(str!="quit"){
        cout << "CharMan_v1.0:$ " ;
        ss.clear();
        getline(cin, str);
        ss.str(str);
        tokens.clear();
        while (ss >> buf) tokens.push_back(buf);
        if(tokens.front()== "list") cout << "Voici la liste des commandes disponibles" << endl
                                    << setw(30) << left << "quit" << "Quitter le programme" << endl
                                    << setw(30) << left <<"affiche_sac" << "Afficher le sac" << endl
                                    << setw(30) << left <<"affiche_equip" << "Afficher l'équipement" << endl
                                    << setw(30) << left <<"creer_objet " << "Creer un objet" << endl
                                    << setw(30) << left <<"creer_sac" << "Creer un sac" << endl
                                    << setw(30) << left << "desequiper" << "desequiper un objet" << endl
                                    << setw(30) << left << "trie_poids" << "Trier le sac par poids" << endl
                                    << setw(30) << left << "trie_stat" << "Trier le sac par stat" << endl
                                    << setw(30) << left << "equiper" << "equiper un objet du sac" << endl
                                    << setw(30) << left << "jeter" << "jeter un objet" << endl
                                    << setw(30) << left << "stats" << "Statistiques du charactere" << endl
                                    << setw(30) << left << "changer_sac" << "Changer le sac" << endl
                                    << setw(30) << left << "apoil" << "Enlever tout l'équipement" << endl
                                    << setw(30) << left << "preset" << "faire un preset de l equipement" << endl
                                    << setw(30) << left << "rappel" << "rappel un preset" << endl
                                    << setw(30) << left << "texte" << "lire un fichier texte"
                                    << endl;
    
        this->Action(tokens);
    }

}
/**
 * enregistre les commandes enregistrées dans shell
 */
void Charactere::Action(vector<string> tokens){

        vector<string> ::iterator ite = tokens.begin();

        bool lu=false;

        if(tokens.front()== "creer_objet"){
           tokens.erase(ite);
           if(tokens.size()!=0){
               this->CreerObjet(tokens);
               lu=true;
               }
        }
        if(tokens.front()== "creer_sac"){
           tokens.at(0)="sac";
           if(tokens.size()!=1){
               this->CreerObjet(tokens);
               lu = true;
           }
        }
        if(tokens.front()== "affiche_sac"){
            this->mySac->AfficheSac();
            lu=true;
        }
        if(tokens.front()== "affiche_equip"){
            this->EquipementCourant->AfficheEquipement();
            lu=true;
        }
        if(tokens.front()== "desequiper") if(tokens.size()>=2){
                                            this->EquipementCourant->choixDesequiper(tokens.at(1));
                                            lu=true;
                                          }
        if(tokens.front()== "trie_poids") if(this->mySac!=NULL) if(this->mySac->Contient.size()>0){
                                                                    this->mySac->OrdonnerPoids();
                                                                    lu=true;
                                                                }
        if(tokens.front()== "trie_stat") if(this->mySac!=NULL) if(this->mySac->Contient.size()>0){
                                                                    this->mySac->RangeStat(tokens.at(1));
                                                                    lu=true;
                                                               }
        if(tokens.front()== "equiper") if(tokens.size()==2){
                                            this->equiper_objet(tokens.at(1));
                                            lu=true;
                                       }
        if(tokens.front()== "jeter") if(tokens.size()>=2){
                                            this->Jeter_objet(tokens.at(1));
                                            lu=true;
                                     }
        if(tokens.front()== "stats"){
            this->AffichePerso();
            lu=true;
        }
        if(tokens.front()== "changer_sac"){
            this->ChangerSac();
            lu=true;
        }
        if(tokens.front()== "apoil"){
            this->ToutNu();
            lu=true;
        }
        if(tokens.front()== "preset"){
            this->EquipementCourant->Enregistrer_Preset();
            lu=true;
        }
        if(tokens.front()== "rappel"){
            this->EquipementCourant->Rappeler();
            lu=true;
        }
        if(tokens.front()== "texte"){

            if(tokens.size()==2){
                fstream fichier((char*)tokens.at(1).c_str());
                if ( !fichier ) {
                    cout << "fichier inexistant";
                } else {
                    string ligne,buf;
                    stringstream ss("dummy");
                    vector<string> tok;

                    while (!fichier.eof()  )
                    {
                        getline( fichier, ligne );
                        ss.str(ligne);
                        while (ss >> buf) tok.push_back(buf);
                        if(tok.size()!=0)this->Action(tok);
                        ss.clear();
                        tok.clear();
                    }
                    
                }
                lu=true;
            }
        }
        if(!lu)cout<<"mauvaise commande\n\n";
}

/**
 * prendre un sac passé en parametre
 * @argument le sac a prendre
 */
void Charactere::PrendreSac(Sac& S){
    if(this->mySac==NULL){
        S.myCharactere=this;
        this->mySac=&S;
    }
    else ChangeSac(S);
}

/**
 * ajouter un objet a l'equipement
 * @argument le nom de l'objet avec lequel s'equiper
 */
bool Charactere::equiper_objet(string nom_objet)
{

    vector<Objet *>::iterator iter = this->mySac->Contient.begin();

    for(int i=0;i<this->mySac->Contient.size();i++){
        if(this->mySac->Contient.at(i)->nom==nom_objet && this->mySac->Contient.at(i)->isEquipable()){
            this->EquipementCourant->equiper(*(ObjetEquipable*)this->mySac->Contient.at(i));
            this->mySac->Contient.erase(iter);
            return true;
        }
        iter++;
    }
    return false;
}

/**
 * revoie le nom de l'armure avec laquelle le presonnage est equipé
 */
string Charactere::getNomArmure(){
    return EquipementCourant->myArmure->getNom();
}


/**
 * modifie le bonus defense (mise a jour) du personnage
 * @argument nouvelle valeur du bonus defensif
 */
void Charactere::setDef(float d)
{
    this->Defense=d;
}
/**
 * modifie le bonus inteligence (mise a jour) du personnage
 * @argument nouvelle valeur du bonus intelligence
 */
void Charactere::setInt(float i)
{
    this->Intelligence=i;
}
/**
 * modifie le bonus furtivite (mise a jour) du personnage
 * @argument nouvelle valeur du bonus furtivité
 */
void Charactere::setFur(float f)
{
    this->Furtivite=f;
}

/**
 * renvoie la chargz max que peut porter le personnage
 */
int Charactere::getChargeMax(){
    return this->ChargeMax;
}

/*
 * change le sac du personnage et retourne true si le changement de sac s'est bien passé, false sinon
 * @argument le nouveau sac
 */
bool Charactere::ChangeSac(Sac &S){

    if(S.getCapacite()<mySac->getObjets().size() && S.Poids_limite<mySac->getPoids()) return false;

    int i;
    S.myCharactere=this;
    for(i=0;i<mySac->getObjets().size();i++){
        S.Ajouter(*(mySac->getObjets().at(i)));
    }

    mySac->getObjets().clear();

    this->mySacDispo.push_back(this->mySac);

    vector< Sac* >::iterator ite = this->mySacDispo.begin();

    for(i=0;i<this->mySacDispo.size();i++){
        if((this->mySacDispo.at(i))==&S){
            break;
        }

        ite++;
    }

    this->mySac=&S;
    S.myCharactere=this;
    this->mySacDispo.erase(ite);


    return true;


}
/**
 * debut de l'action de changer de sac, propose les differents choix de sacs possibles
 */
void Charactere::ChangerSac()
{

    string tmp;
    istringstream iss;
    int ID;

    cout<<"ID \tcapacite limite du sac\tpoids limite du sac"<<endl<<endl;

    if(this->mySacDispo.size()==0) cout<<"pas de sac disponible\n"<<endl;
    else{

        for(int i=0;i<this->mySacDispo.size();i++){
            cout << i << " : \t";
            cout<<this->mySacDispo.at(i)->Capacite<<"\t"<<this->mySacDispo.at(i)->Poids_limite<<"\t"<<endl;
        }

        cout<<endl;
        cout<< "Entrez l'ID de l'objet : ";
        getline(cin,tmp);
        iss.str(tmp);
        iss>>ID;
        iss.clear();

        if(ID<mySacDispo.size() &&  ID>=0){
            Sac *s=this->mySacDispo.at(ID);
            if(!this->ChangeSac(*s))cout<<"impossible de changer de sac"<<endl;
            else cout<<"sac change"<<endl;
        }
        else cout<<"mauvaise ID... \n"<<endl;
    }
}

/**
 * renvoie la charge actuelle portée par le personnage
 */
int Charactere::getPoidsPorte(){
    return this->mySac->getPoids()+this->EquipementCourant->getPoids();
}

/**
 * crée un objet avec en parametre toutes les caractéristiques de ce nouvel objet
 * @argument les caracteristique de l'objet sous forme de vecteur de string
 */
void Charactere::CreerObjet(vector<string> tokens){

     int poids;

    
     
     

     if(tokens.size()==3){
     istringstream iss(tokens.at(2));
     iss>>poids;
     iss.clear();
         if(tokens.front()=="non_equipable"){
             if(!this->mySac->Ajouter(* new ObjetNonEquipable(tokens.at(1),poids)))cout<<"objet trop lourd ou sac plein"<<endl;
         }
         else if(tokens.front()=="sac"){
             int capa;
             iss.str(tokens.at(1));
             iss>>capa;
             iss.clear();
             this->mySacDispo.push_back(new Sac(capa,poids));
         }
         else cout<<"mauvais arguments\n\n";
     }
     if(tokens.size()==6){
     istringstream iss(tokens.at(2));
     iss>>poids;
     iss.clear();
     float inte;
     float fur;
     float def;

     iss.str(tokens.at(3));
     iss>>inte;
     iss.clear();
     iss.str(tokens.at(4));
     iss>>fur;
     iss.clear();
     iss.str(tokens.at(5));
     iss>>def;
     iss.clear();

         if(tokens.front()=="armure"){
             if(!this->mySac->Ajouter(* new Armure(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         if(tokens.front()=="bague"){
             if(!this->mySac->Ajouter(* new Bague(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         if(tokens.front()=="bottes"){
             if(!this->mySac->Ajouter(* new Bottes(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         if(tokens.front()=="chapeau"){
             if(!this->mySac->Ajouter(* new Chapeau(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         if(tokens.front()=="ceinture"){
             if(!this->mySac->Ajouter(* new Ceinture(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         if(tokens.front()=="collier"){
             if(!this->mySac->Ajouter(* new Collier(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         if(tokens.front()=="combinaison"){
             if(!this->mySac->Ajouter(* new Combinaison(tokens.at(1),inte,fur,def,poids))) cout<<"objet trop lourd ou sac plein"<<endl;
         }
         
     }
}

/**
 * affiche les caracteristiques du personnage
 */
void Charactere::AffichePerso(){
    cout<<"nom :     lutin des prairies du sud de la mer du nord"<<endl;
    cout<<"Intelligence \t\t"<<this->Intelligence<<endl;
    cout<<"Furtivite \t\t"<<this->Furtivite<<endl;
    cout<<"Defense \t\t"<<this->Defense<<endl;
    cout<<"charge max possible \t"<<this->ChargeMax<<endl;
    cout<<"charge portee \t\t"<<this->getPoidsPorte()<<endl<<endl;

}

/**
 * jete un objet porté par le personnage, soit dans le sac, soit comme equipement
 * @argument le nom de l'objet a jeter
 */
void Charactere::Jeter_objet(string nom_objet){

    bool jeter = false;

    vector<Objet *>::iterator iter = this->mySac->Contient.begin();

    for(int i=0;i<this->mySac->Contient.size();i++){
        if(this->mySac->Contient.at(i)->nom==nom_objet){
            cout<<"objet jeté\n";
            this->mySac->Contient.erase(iter);
            jeter=true;
            break;
        }
        iter++;
    }
    if(!jeter){
        if(this->EquipementCourant->myCombinaison!=NULL && this->EquipementCourant->myCombinaison->nom==nom_objet){
           this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myCombinaison);
           this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myChapeau!=NULL && this->EquipementCourant->myChapeau->nom==nom_objet) {
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myChapeau);
            this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myBague1!=NULL && this->EquipementCourant->myBague1->nom==nom_objet) {
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myBague1);
            this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myBague2!=NULL && this->EquipementCourant->myBague2->nom==nom_objet) {
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myBague2);
            this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myCollier!=NULL && this->EquipementCourant->myCollier->nom==nom_objet){
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myCollier);
            this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myCeinture!=NULL && this->EquipementCourant->myCeinture->nom==nom_objet){
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myCeinture);
            this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myArmure!=NULL && this->EquipementCourant->myArmure->nom==nom_objet){
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myArmure);
            this->Jeter_objet(nom_objet);
        }
        if(this->EquipementCourant->myBottes!=NULL && this->EquipementCourant->myBottes->nom==nom_objet) {
            this->EquipementCourant->desequiper(*(ObjetEquipable*)this->EquipementCourant->myBottes);
            this->Jeter_objet(nom_objet);
        }
    }


}

/**
 * retirer tout l'équipement du personnage
 */
void Charactere::ToutNu(){
    //la fonction ajoute au sac les objet par ordre croissant de bonus total si il ny a plus de place, les objet sont jetes
    std::vector< Objet* > Obj_equ;

    if(this->EquipementCourant->myCombinaison!=NULL){
       Obj_equ.push_back(this->EquipementCourant->myCombinaison);
       this->EquipementCourant->myCombinaison=NULL;
    }
    if(this->EquipementCourant->myChapeau!=NULL) {
       Obj_equ.push_back(this->EquipementCourant->myChapeau);
       this->EquipementCourant->myChapeau=NULL;
    }
    if(this->EquipementCourant->myBague1!=NULL) {
       Obj_equ.push_back(this->EquipementCourant->myBague1);
       this->EquipementCourant->myBague1=NULL;
    }
    if(this->EquipementCourant->myBague2!=NULL){
       Obj_equ.push_back(this->EquipementCourant->myBague2);
       this->EquipementCourant->myBague2=NULL;
    }
    if(this->EquipementCourant->myCollier!=NULL){
       Obj_equ.push_back(this->EquipementCourant->myCollier);
       this->EquipementCourant->myCollier=NULL;
    }
    if(this->EquipementCourant->myCeinture!=NULL){
       Obj_equ.push_back(this->EquipementCourant->myCeinture);
       this->EquipementCourant->myCeinture=NULL;
    }
    if(this->EquipementCourant->myArmure!=NULL){
       Obj_equ.push_back(this->EquipementCourant->myArmure);
       this->EquipementCourant->myArmure=NULL;
    }
    if(this->EquipementCourant->myBottes!=NULL) {
       Obj_equ.push_back(this->EquipementCourant->myBottes);
       this->EquipementCourant->myBottes=NULL;
    }

    if(Obj_equ.size()!=0){

        bool Place = true;
        vector< Objet* >::iterator M,ite;
        int maxi;
        float max,bonus_total;

        while(Place){
            Place=false;
            max=-10000;
            maxi=-1;
            ite = Obj_equ.begin();

            for(int i=0;i<Obj_equ.size();i++){
                bonus_total=Obj_equ.at(i)->getObjetStat("int")+Obj_equ.at(i)->getObjetStat("def")+Obj_equ.at(i)->getObjetStat("fur");
                if(bonus_total>=max){
                    max=bonus_total;
                    M=ite;
                    maxi=i;
                }
                ite++;
            }

            if(maxi!=-1){
                if((Obj_equ.at(maxi)->poids+this->mySac->getPoids())<=this->mySac->getPoidsLimite())Place=this->mySac->Ajouter(*Obj_equ.at(maxi)); //on verifie que l objet n est pas trop lourd pour le sac
                Obj_equ.erase(M);
                Place=true;
               
            }
        }

        Obj_equ.clear();
    }

    cout<<"exhibitioniste!!!!\n\n";
}
